/*
  Verification Process:
  - Start verification from other device.
  - Run: `window.mClient.getVerificationRequestsToDeviceInProgress(userId)[0].accept();`
  - The other device will then ask about doing a QR or emoji thing. Select emoji.
  - Run `window.mClient.getVerificationRequestsToDeviceInProgress(userId)[0].verifier.doVerification();`
  - Once you see the emojis on the other device, the emoji data will appear in `window.mClient.getVerificationRequestsToDeviceInProgress(userId)[0].verifier.sasEvent.sas.emoji`. There is also a `.decimal` prop in the `sas` object.
  - Probably should display those emojis to so you can compare.
  - Run `window.mClient.getVerificationRequestsToDeviceInProgress(userId)[0].verifier.sasEvent.confirm();`
  - You're verified!
*/

const LOCAL_STORAGE_SESSION_KEY = 'matrix-session';

var initSync = false;
var highlightedRoom = 0;
var rooms = [];
var highlightedMsg;
var page = 'login';
var currentRoom;
var highlightedLoginInput = 0;
var scrollMsg;
var editingEvent;
var replyingToEvent;
var previousConv;
var mainMenu = [];
var highlightedMenuItem = 0;
var verificationPhase = null;
var currentlyWatching = null;
var previousPage = 'rooms';
var loadingMessagesIndex = 0;

const swapMiddle = () => {
  document.getElementById('middle').innerHTML = 'REPLAY';
};

const backToChat = () => {
  const chat = document.getElementById('chat');
  chat.style.display = 'flex';
  document.getElementById('chat-textbox').focus();
}

const parseBody = (body) => {
  if(body) {
    const reg = /\b(https|http)?:\/\/\S+/gi;
    const urls = body.match(reg);
    const withoutUrls = body.split(reg);
    var i = -1;
    const parsedBody = withoutUrls.reduce((a, str) => {
      if(str === 'https' || str === 'http') {
        i++;
        return a + `<a target="_blank" href="${urls[i]}">${urls[i]}</a>`;
      } else {
        return a + str;
      }
    }, '');
    return parsedBody;

  }
  return body;
}

const getFormattedBody = (content = {}) => {
  if(content.formatted_body && content.formatted_body.indexOf('<mx-reply>') > -1) {
    const fBodyformattedBody = content.formatted_body;
    const mxReplyIndex = fBodyformattedBody.indexOf('</mx-reply>');
    const body = fBodyformattedBody && fBodyformattedBody.slice(mxReplyIndex + 11);
    const formattedBody = fBodyformattedBody && fBodyformattedBody.substring(0, mxReplyIndex).substring(10) + '<br/>' + body;
    return [formattedBody, body];
  }
  const parsedBody = parseBody(content.body);
  return [parsedBody, content.body];
};

const ALLOWED_BLOB_MIMETYPES = [
    'image/jpeg',
    'image/gif',
    'image/png',
    'image/apng',
    'image/webp',
    'image/avif',

    'video/mp4',
    'video/webm',
    'video/ogg',
    'video/quicktime',

    'audio/mp4',
    'audio/webm',
    'audio/aac',
    'audio/mpeg',
    'audio/ogg',
    'audio/wave',
    'audio/wav',
    'audio/x-wav',
    'audio/x-pn-wav',
    'audio/flac',
    'audio/x-flac',
];

const getBlobSafeMimeType = (mimetype) => {
  if (!ALLOWED_BLOB_MIMETYPES.includes(mimetype)) {
    return 'application/octet-stream';
  }
  return mimetype;
};

const getBlobData = (id, url, content, type) => {
  let file;
  let mimetype;
  if(type === 'image') {
    mimetype = content.info.mimetype ? content.info.mimetype.split(";")[0].trim() : '';
    file = content.file;
  } else if(type === 'video') {
    mimetype = content.info.thumbnail_info.mimetype ? content.info.thumbnail_info.mimetype.split(";")[0].trim() : '';
    file = content.info.thumbnail_file;
  } else if (type === 'audio') {
    mimetype = content.info.mimetype ? content.info.mimetype.split(";")[0].trim() : '';
    file = content.file;
  } else {
    return;
  }
  fetch(url).then((res) => {
    res.arrayBuffer().then((a) => {
      decryptAttachment(a, file).then((dataArray) => {
        mimetype = getBlobSafeMimeType(mimetype);
        const blob = new Blob([dataArray], { type: mimetype });
        const blobUrl = URL.createObjectURL(blob);
        const image = document.getElementById(`image-${id}`);
        image.src = blobUrl;
        if(type === 'video') {
          image.setAttribute('video', JSON.stringify(content));
        } else if( type === 'audio') {
          image.setAttribute('audio', JSON.stringify(content));
        }
      });
    })
  });
};

const messageBuilder = (event, room) => {
  const content = event.getContent();
  const sender = event.getSender();
  const date = event.getDate();
  const dateString = date.toLocaleString();
  const relation = event.getRelation();
  const members = room ? room.getMembers() : [];
  const shouldDisplayName = sender !== window.mClient.getUserId() && members.length > 2;
  const user = shouldDisplayName && room.getMember(sender);
  const floatDirection = sender === window.mClient.getUserId() ?
        'align-self:flex-end;margin-right:10px;background-color:#4dc860' :
        'align-self:flex-start;margin-left:10px;background-color:white';
  const dateDirection = sender === window.mClient.getUserId() ?
        'text-align:right;margin-right:10px;' :
        'text-align:left;margin-left:10px;';
  const reactDirection = sender === window.mClient.getUserId() ?
        'justify-content:flex-end;margin-right:10px;' :
        'justify-content:flex-start;margin-left:10px;';
  const [formattedBody, realBody] = getFormattedBody(content);
  const isSending = event.getId().indexOf(currentRoom) > -1;
  const dateOrSending = isSending ? `<p id="datetime-${event.getId()}" style="font-size:10px;color:gray;${dateDirection}" datetime="${dateString}">Sending...</p>`: `<p id="datetime-${event.getId()}" style="font-size:10px;color:gray;${dateDirection}">${dateString}</p>`;
  const nameOrNothing = user ? `<p id="name-${event.getId()}" style="font-size:10px;color:gray;${dateDirection}" datetime="${dateString}">${user.name}</p>` : '';
  window.mClient.sendReadReceipt(event, "m.read");
  if(content.msgtype === 'm.image') {
    const imageUrl = content.url ? window.mClient.mxcUrlToHttp(content.url) : window.mClient.mxcUrlToHttp(content.file.url);
    if (!content.url) {
      getBlobData(event.getId(), imageUrl, content, 'image');
    }
    return `
<div id="msg-${event.getId()}" class="msgItem" sender="${sender}">
<div id="innermsg-${event.getId()}" class="msgInnerItem" style="${floatDirection}">
<img id="image-${event.getId()}" src="${imageUrl}" height=100 o_h="${content.info.h}" o_w="${content.info.w}" alt="${content.body}">
</img>
</div>
${dateOrSending}
<div id="reactions-${event.getId()}" class="reactions" style="${reactDirection}"></div>
</div>
`;
  } else if(content.msgtype === 'm.video') {
    const imageUrl = content.info.thumbnail_file ? window.mClient.mxcUrlToHttp(content.info.thumbnail_file.url) : window.mClient.mxcUrlToHttp(content.info.thumbnail_url);
    if (content.info.thumbnail_file) {
      getBlobData(event.getId(), imageUrl, content, 'video');
    } else {
      setTimeout(() => {
        const image = document.getElementById(`image-${event.getId()}`);
        image.setAttribute('video', JSON.stringify(content));
      }, 200);
    }
    return `
<div id="msg-${event.getId()}" class="msgItem" sender="${sender}">
${nameOrNothing}
<div id="innermsg-${event.getId()}" class="msgInnerItem" style="${floatDirection}">
<div class="play-button-container">
<div class="play-button">
▶
</div>
</div>
<img id="image-${event.getId()}" src="${imageUrl}" height=100 alt="${content.body}">
</img>
</div>
${dateOrSending}
<div id="reactions-${event.getId()}" class="reactions" style="${reactDirection}"></div>
</div>
`;
  } else if(content.msgtype === 'm.audio') {
    const imageUrl = content.url ? window.mClient.mxcUrlToHttp(content.url) : window.mClient.mxcUrlToHttp(content.file.url);
    if (!content.url) {
      getBlobData(event.getId(), imageUrl, content, 'audio');
    } else {
      setTimeout(() => {
        const image = document.getElementById(`image-${event.getId()}`);
        image.setAttribute('audio', JSON.stringify(content));
      }, 200);
    }
    return `
<div id="msg-${event.getId()}" class="msgItem" sender="${sender}">
${nameOrNothing}
<div id="innermsg-${event.getId()}" class="msgInnerItem" style="${floatDirection}">
<pre id="image-${event.getId()}" audio="${JSON.stringify(content)}">
▶ ${content.body}
</pre>
</div>
${dateOrSending}
<div id="reactions-${event.getId()}" class="reactions" style="${reactDirection}"></div>
</div>
`;
  } else {
    return `
<div id="msg-${event.getId()}" class="msgItem" sender="${sender}">
${nameOrNothing}
<div id="innermsg-${event.getId()}" class="msgInnerItem" style="${floatDirection}" body="${realBody}">
<pre>
${formattedBody}
</pre>
</div>
${dateOrSending}
<div id="reactions-${event.getId()}" class="reactions" style="${reactDirection}"></div>
</div>
`

  }

};

const gotoRoomView = () => {
  if(initSync) {
    highlightedRoom = 0;
  }
  const roomz = window.mClient.getRooms();
  const roomList = roomz.sort((roomA, roomB) => {
    return roomA.name.localeCompare(roomB.name);
  }).reduce((a, room) => {
    if (a === '<div>') {
      return a + `
<div id="room-${room.roomId}" class="roomItem active">
${room.name}
</div>
<br/>
`;
    } else {
      return a + `
<div id="room-${room.roomId}" class="roomItem">
${room.name}
</div>
<br/>
`;
    }
  }, '<div>'); + '</div>'
  document.getElementById('room-menu').style = 'display: unset';
  document.getElementById('chat').style = 'display: none';
  document.getElementById('menu').style = 'display: none';
  document.getElementById('chat-textbox').value = '';
  document.getElementById('chat-textbox').selectionStart = 0
  document.getElementById('chat-textbox').style.height = '24px';
  document.getElementById('conversation').style.height = '100%';
  document.getElementById('editing').style.bottom = '55px';
  document.getElementById('replying').style.bottom = '55px';
  document.getElementById('login').style = 'display: none';
  document.getElementById("left").innerHTML = "";
  document.getElementById("right").innerHTML = "Menu";
  document.getElementById("middle").innerHTML = "SELECT";
	document.getElementById("title-text").innerHTML = 'Rooms';
  document.getElementById("room-menu").innerHTML = roomList;
  initSync = true;
  rooms = document.getElementsByClassName('roomItem');
  page = 'rooms';
  highlightedMsg = null;
};

const markReaction = (event, relation) => {
  const {
    event_id,
    rel_type,
    key
  } = relation;
  const msgItem = document.getElementById(`msg-${event_id}`);
  const reactionsEl = document.getElementById(`reactions-${event_id}`);
  if(msgItem && reactionsEl) {
    reactionsEl.innerHTML = reactionsEl.innerHTML + `
<div id="react-${event.getId()}" class="${relation.key} ${event.getSender()}" style="font-size:16px;">
${relation.key}
</div>
`
    // if the last message gets reacted, scroll down to see it
    const msgs = document.getElementsByClassName('msgItem');
    if(msgItem === msgs[msgs.length - 1]) {
      const conversationEl = document.getElementById("conversation");
      conversationEl.scrollTo(0, conversationEl.scrollHeight);
    }
  }
};

const removeReaction = (eventId) => {
  const reaction = document.getElementById(`react-${eventId}`);
  if(reaction)
    reaction.remove();
};

const replaceBody = (event, relation) => {
  const content = event.getContent();
  const innerMsgItem = document.getElementById(`innermsg-${relation.event_id}`);
  const newBody = content['m.new_content'].body;
  const hasFormattedBody = innerMsgItem.innerHTML.indexOf('blockquote') > -1;
  const parsedBody = parseBody(newBody);
  const allOfIt = parsedBody + '<br/><b class="edited-marker" style="color:black">(edited)</b>';
  if(innerMsgItem) {
    innerMsgItem.setAttribute('body', newBody);
    if(hasFormattedBody) {
      const blockquote = innerMsgItem.getElementsByTagName('blockquote')[0];
      innerMsgItem.innerHTML = `
<pre>
${blockquote.outerHTML}
<br/>
${allOfIt}
`;
    } else {
      innerMsgItem.innerHTML = `
<pre>
${allOfIt}
</pre>
`;
    }
  }
};

var timeout;
const prepareSession = (client) => {
  document.getElementById("room-menu").innerHTML = "Loading..."
	document.getElementById("title-text").innerHTML = 'Rooms';
  document.getElementById("left").innerHTML = "";
  document.getElementById("right").innerHTML = "";
  document.getElementById("middle").innerHTML = "SELECT";

  window.mClient = window.matrixcs.createClient({
    baseUrl: client.baseUrl,
    accessToken: client.access_token,
    userId: client.user_id,
    deviceId: client.device_id,
    sessionStore: new window.matrixcs.WebStorageSessionStore(localStorage),
  });
  window.mClient.on("sync", function(event) {
    if(!initSync) {
      gotoRoomView();
    }
  });
  window.mClient.on("crypto.verification.request", function(event, room, toStartOfTimeline) {
    if(verificationPhase === 'accept') {
      verificationPhase = 'sas';
      event.accept().then(() => {
        alert('Accepted verification! On the other device, select emoji verification, then press ok here.');
        event.waitFor((e) => {
          if(e.verifier && verificationPhase) {
            verificationPhase = 'sas';
            e.verifier.doVerification();
            setTimeout(() => {
              if(e.verifier.sasEvent) {
                const emojis = e.verifier.sasEvent.sas.emoji.reduce((a, em) => {
                  return a + em[0] + ' ' + em[1] + '\n';
                }, '');
                if(confirm('Match?\n' + emojis)) {
                  e.verifier.sasEvent.confirm();
                } else {
                  e.verifier.sasEvent.mismatch();
                }
              }
            }, 3000);
            clearTimeout(timeout);
          }
        });
        timeout = setTimeout(() => {
          verificationPhase = null;
        }, 2 * 60 * 1000);      // you get 2 minutes
      }).catch(() => {
        alert('Failed to accept verification!');
        verificationPhase = null;
      });
    }
  });
  window.mClient.on("Room.timeline", function(event, room, toStartOfTimeline) {
    window.mClient.decryptEventIfNeeded(event).then(() => {
      const eventType = event.getType();
      const relation = event.getRelation();
      if (page === 'chat' && currentRoom === room.roomId ) {
        switch(eventType) {
        case 'm.room.message': {
          if(!relation) {
            const newMsg = messageBuilder(event, room);
            const conversationEl = document.getElementById("conversation");
            const typing = document.getElementsByClassName('typing');
            let newTyping = '';
            for(var i = 0; i < typing.length; i++) {
              const div = typing[i];
              div.remove();
              newTyping = newTyping + div.outerHTML;
            }
            if(loadingMessagesIndex > 0) {
              conversationEl.innerHTML = newMsg + conversationEl.innerHTML + newTyping;
              loadingMessagesIndex = loadingMessagesIndex - 1;
              highlightedMsg = highlightedMsg + 1;
            } else {
              conversationEl.innerHTML = conversationEl.innerHTML + newMsg + newTyping;
            }
            if(document.activeElement.id === 'chat-textbox') {
              conversationEl.scrollTo(0, conversationEl.scrollHeight);
            } else if(loadingMessagesIndex === 0) {
              const cur = document.getElementsByClassName('msgItem msgItem-selected')[0];
              cur.scrollIntoView({
                behavor: 'smooth',
                block: 'bottom',
                inline: 'nearest'
              });
            }
          } else {
            if(relation.rel_type === 'm.replace')
              replaceBody(event, relation);
          }
          return;
        }
        case 'm.reaction': {
          if(loadingMessagesIndex > 0) {
            loadingMessagesIndex = loadingMessagesIndex - 1;
          }
          if(relation && relation.rel_type === 'm.annotation') {
            markReaction(event, relation);
          }
          return;
        }
        case 'm.room.redaction': {
          if(loadingMessagesIndex > 0) {
            loadingMessagesIndex = loadingMessagesIndex - 1;
          }
          removeReaction(event.event.redacts);
          return;
        }
        }
      }
    });
  });
  window.mClient.on("RoomMember.typing", function(event, member) {
    const room = window.mClient.getRoom(currentRoom);
    if(room) {
      const members = room.getMembers().map(({userId}) => userId);
      if (page === 'chat' && members.indexOf(member.userId) > -1) {
        if (member.typing) {
          const typingDiv = `<div id="typing-${member.userId}" class="typing">${member.name} is typing...</div>`;
          const conv = document.getElementById('conversation');
          conv.innerHTML = conv.innerHTML + typingDiv;
          // if(conv.clientHeight - conv.scrollTop < 20) {
          //   conv.scrollTo(0, conv.scrollHeight);
          // }
        }
        else {
          const typingDiv = document.getElementById(`typing-${member.userId}`);
          if(typingDiv)
            typingDiv.remove();
        }
      }
    }
  });
  // window.mClient.on("Room.receipt", function(event, member) {
  //   const content = event.getContent();
  //   const reads = Object.keys(content);
  //   reads.forEach((eventId) => {
  //     if(page === 'chat') {
  //       const msg = document.getElementById(`msg-${eventId}`);
  //       if(member.userId !== msg.getAttribute('sender')) {
  //         const dateTime = document.getElementById(`datetime-${eventId}`);
  //         if(dateTime)
  //           dateTime.innerHTML = 'read <br/>' + dateTime.innerHTML;
  //       }
  //     }
  //   })
  // })
  window.mClient.initCrypto().then(() => {
    window.mClient.startClient({
      initialSyncLimit: 10,
      lazyLoadMembers: true,
    });
  });
};

const login = (baseUrl, user, password) => {
  const client = window.matrixcs.createClient(baseUrl);
  client.loginWithPassword(user, password).then((d) => {
    const session = {...d, baseUrl};
    localStorage.setItem(LOCAL_STORAGE_SESSION_KEY, JSON.stringify(session));
    prepareSession(session);
  }).catch((e) => {
    console.error(e);
    alert('Login error! Check all inputs.')
    document.getElementById('middle').innerHTML = 'LOGIN';
  });
};

const sendReaction = (emoji) => {
  const el = document.getElementsByClassName('msgItem msgItem-selected')[0];
  if(el) {
    if(el.innerHTML.indexOf(emoji) > -1) { // has one already
      const react = el.getElementsByClassName(`${emoji} ${window.mClient.getUserId()}`)[0];
      if (react) {
        const eventId = react.id.substring(6);
        return window.mClient.redactEvent(currentRoom, eventId).then(() => {
          console.log('redacted!');
        }).catch((e) => {
          console.log(e);
          alert('Failed to redact reaction to message: ', JSON.stringify(e));
        });
      }
    }
    const event_id = el.id.substring(4);
    const content = {
      'm.relates_to': {
        key: emoji,
        event_id,
        rel_type: 'm.annotation',
      }
    };
    const date = new Date();
    const tnxId = date.getTime()/1000;
    window.mClient.sendEvent(currentRoom, "m.reaction", content, tnxId).then(({event_id}) => {
      const room = window.mClient.getRoom(currentRoom);
      const newReact = document.getElementById(`react-~${room.roomId}:${tnxId}`);
      newReact.id = `react-${event_id}`;
    }).catch((e) => {
      console.log(e);
      alert(e.message);
    });
  }
};

const killSession = () => {
  window.mClient.stopClient();
  localStorage.removeItem(LOCAL_STORAGE_SESSION_KEY);
  close();
};

const verifyDevice = () => {
  if(confirm('Before starting the verification process on the other device, press okay here and wait.')) {
    verificationPhase = 'accept';
  }
}

const verifyDevices = () => {
  if(confirm('If this error was because of unverified devices, would you like to verify all devices in this room?')) {
    const members = window.mClient.getRoom(currentRoom).getMembers();
    Promise.all(members.map((member) => {
      const userId = member.userId;
      const devices = window.mClient.getStoredDevicesForUser(userId);
      devices.filter(({verified}) => verified === 0).map((dev) => {
        return window.mClient.setDeviceVerified(userId, dev.deviceId, true);
      });
    })).then(() => {
      alert('All devices in this room are now marked as verified!');
    }).catch(() => {
      alert('An error occurred while marking devices as verified.');
    });
  }
}

const openMenu = () => {
  page = 'menu';
  document.getElementById("left").innerHTML = "";
  document.getElementById("right").innerHTML = "Back";
  document.getElementById("middle").innerHTML = "SELECT";
  const menuList = mainMenu && mainMenu.reduce((a, item) => {
    if (a === '') {
      return a + `
<div id="menu-${item.label}" class="menuItem active">
${item.label}
</div>
<br/>
`;
    } else {
      return a + `
<div id="menu-${item.label}" class="menuItem">
${item.label}
</div>
<br/>
`;
    }
  }, '');
  const menu = document.getElementById('menu');
  const room = document.getElementById('room-menu');
  const chat = document.getElementById('chat');
  const input = document.getElementById('chat-textbox');
  menu.style.display = 'unset';
  room.style.display = 'none';
  chat.style.display = 'none';
  input.blur();
  menu.innerHTML = menuList;
};

const closeMenu = (goBack) => {
  page = previousPage;
  const menu = document.getElementById('menu');
  menu.style.display = 'none';
  goBack();
};

const progressHandler = ({loaded, total}) => {
  const percent = Math.round(loaded / total * 100);
  const uploading = document.getElementById('uploading');
  uploading.style.bottom = "55px";
  uploading.style.display = "unset";
  uploading.innerHTML = percent + "%";
};

const loadingHandler = () => {
  const chatBox = document.getElementById('chat-textbox');
  const uploading = document.getElementById('uploading');
  uploading.style.bottom = (chatBox.scrollHeight + 55 - 29) + "px";
  uploading.style.display = "unset";
  uploading.innerHTML = "Loading...";
};

const uploadContent = (file, img) => {
  const uploading = document.getElementById('uploading');
  return new Promise((res, rej) => {
    if(window.mClient.isRoomEncrypted(currentRoom)) {
      const reader = new FileReader();
      reader.onload = () => {
        const attachment = encryptAttachment(reader.result).then((encryptedResult) => {
          const blob = new Blob([encryptedResult.data]);
          window.mClient.uploadContent(blob, {
            includeFilename: false,
            progressHandler,
          }).then((url) => {
            uploading.style.display = "none";
            let content;
            if(file.type.indexOf('image') > -1) {
              content = {
                msgtype: 'm.image',
                body: file.name,
                info: {
                  h: img.height,
                  w: img.width,
                  mimetype: file.type,
                  size: file.size,
                },
                file: {
                  ...encryptedResult.info,
                  url,
                }
              };
            } else if(file.type.indexOf('video') > -1) {
              content = {
                msgtype: 'm.video',
                body: file.name,
                info: {
                  mimetype: file.type,
                  size: file.size,
                },
                file: {
                  ...encryptedResult.info,
                  url,
                }
              };
            } else if(file.type.indexOf('audio') > -1) {
              content = {
                msgtype: 'm.audio',
                body: file.name,
                info: {
                  mimetype: file.type,
                  size: file.size,
                },
                file: {
                  ...encryptedResult.info,
                  url,
                }
              };
            }
            res(content);
          }).catch((e) => {
            uploading.style.display = "none";
            alert('Failed to upload file!');
          });
        });
      };
      reader.readAsArrayBuffer(file);
    } else {
      window.mClient.uploadContent(file, {
        progressHandler,
      }).then((url) => {
        uploading.style.display = "none";
        let content;
        if(file.type.indexOf('image') > -1) {
          content = {
            msgtype: 'm.image',
            body: file.name,
            url,
            info: {
              h: img.height,
              w: img.width,
              mimetype: file.type,
              size: file.size,
            }
          };
        } else if(file.type.indexOf('video') > -1) {
          content = {
            msgtype: 'm.video',
            body: file.name,
            url,
            info: {
              mimetype: file.type,
              size: file.size,
            }
          };
        } else if(file.type.indexOf('audio') > -1) {
          content = {
            msgtype: 'm.audio',
            body: file.name,
            url,
            info: {
              mimetype: file.type,
              size: file.size,
            }
          };
        }
        res(content);
      }).catch((e) => {
        uploading.style.display = "none";
        alert('Failed to upload file!');
      });
    }
  });
}

const insertIntoTextbox = (v) => () => {
  const chatBox = document.getElementById('chat-textbox');
  chatBox.value = chatBox.value + v;
  closeMenu(backToChat);
};

const keydownListener = (e) => {
  const chatBox = document.getElementById('chat-textbox');
  if(page === 'login') {
    if(e.key === 'ArrowUp') {
      var inputs = document.getElementsByClassName('login-text');
      inputs[highlightedLoginInput].blur();
      highlightedLoginInput = highlightedLoginInput - 1;
      if(highlightedLoginInput === -1) {
        highlightedLoginInput = inputs.length - 1;
      }
      inputs[highlightedLoginInput].focus();
    } else if (e.key === 'ArrowDown') {
      var inputs = document.getElementsByClassName('login-text');
      inputs[highlightedLoginInput].blur();
      highlightedLoginInput = highlightedLoginInput + 1;
      if(highlightedLoginInput === inputs.length) {
        highlightedLoginInput = 0
      }
      inputs[highlightedLoginInput].focus();
    } else if (e.key === 'Enter' && document.getElementById('middle').innerHTML !== 'Loading...') {
      var baseUrl = document.getElementById('homeserver').value;
      var user = document.getElementById('user').value;
      var password = document.getElementById('password').value;
      document.getElementById('middle').innerHTML = 'Loading...'
      login(baseUrl, user, password);
    }
    return e;
  } else if(document.activeElement === chatBox) {
    const editing = document.getElementById('editing');
    const replying = document.getElementById('replying');
    const uploading = document.getElementById('uploading');
    if(e.key === 'End' || e.key === 'EndCall') {
      const chatBox = document.getElementById('chat-textbox');
      if(chatBox && chatBox.value !== '') {
        if(confirm('Are you sure you want to close the app?')) {
          window.mClient.stopClient();
          close();
        }
        return;
      }
      return;
    } else if(e.key === 'ArrowUp' && chatBox.selectionStart === 0) {
      chatBox.blur();
      var msgs = document.getElementsByClassName('msgItem');
      const cur = document.getElementsByClassName('msgItem msgItem-selected')[0];
      highlightedMsg = msgs.length - 1;
      msgs[highlightedMsg].className = 'msgItem msgItem-selected';
      msgs[highlightedMsg].scrollIntoView({
        behavor: 'smooth',
        block: 'center',
        inline: 'nearest'
      });
      if(cur)
        cur.className = 'msgItem';
    } else if (e.key === 'Call' || e.key === 'F1') {
      var msgs = document.getElementsByClassName('msgItem');
      if(msgs[highlightedMsg]) {
        chatBox.blur();
        highlightedMsg = highlightedMsg - 1;
        msgs[highlightedMsg].scrollIntoView({
          behavor: 'smooth',
          block: 'center',
          inline: 'nearest'
        });
      }
    } else if(e.key === 'Backspace' && chatBox.selectionStart === 0) {
      e.preventDefault();
      chatBox.blur();
      gotoRoomView(chatBox);
    } else if ((e.key === "SoftLeft" || e.key === "Control") && page === 'chat' && document.getElementById("left").innerHTML !== '') {
      if (editing.style.display === 'unset') { // editing
        const body = document.getElementById('chat-textbox').value.trim();
        const content = {
          body: ' * ' + body,
          msgtype: "m.text",
          'm.relates_to': {
            event_id: editingEvent,
            rel_type: 'm.replace',
          },
          'm.new_content': {
            body,
            msgtype: 'm.text',
          }
        };
        if (body !== '') {
          const date = new Date();
          const tnxId = date.getTime()/1000;
          loadingHandler();
          window.mClient.sendEvent(currentRoom, "m.room.message", content, tnxId).then(({event_id}) => {
            document.getElementById("left").innerHTML = "Send";
            document.getElementById("right").innerHTML = "Menu";
            document.getElementById("middle").innerHTML = "ENTER";
            document.getElementById("chat-textbox").value = '';
            document.getElementById("chat-textbox").disabled = false;
            document.getElementById("chat-textbox").style.height = "24px";
            document.getElementById('chat-textbox').selectionStart = 0
            document.getElementById('conversation').style.height = '100%';
            document.getElementById('editing').style.bottom = "55px";
            editing.style.display = 'none';
            uploading.style.display = 'none';
            editingEvent = null;
            // const msg = document.getElementsByClassName('msgItem msgItem-selected')[0];
            // msg.className = 'msgItem';
            // highlightedMsg = null;

            // const room = window.mClient.getRoom(currentRoom);
            // const newMsg = document.getElementById(`msg-~${room.roomId}:${tnxId}`);
            // const newInnerMsg = document.getElementById(`innermsg-~${room.roomId}:${tnxId}`);
            // newMsg.id = `msg-${event_id}`;
            // newInnerMsg.id = `innermsg-${event_id}`;
            return;
          }).catch((err) => {
            alert('Failed to send message: ' + err.message);
            if(err.name === 'UnknownDeviceError') {
              verifyDevices();
            }
            console.log(err);
            document.getElementById("left").innerHTML = "Send";
            document.getElementById("right").innerHTML = "Menu";
            document.getElementById("middle").innerHTML = "ENTER";
            document.getElementById("chat-textbox").disabled = false;
            document.getElementById("chat-textbox").focus();
          });
        }
      } else if (replying.style.display === 'unset') { // replying
        const body = document.getElementById('chat-textbox').value.trim();
        const content = {
          body: `> <${replyingToEvent.sender}> ${replyingToEvent.body}` + '\n\n' + body,
          format: 'org.matrix.custom.html',
          formatted_body: `<mx-reply><blockquote><a href=\"https://matrix.to/#/${currentRoom}/${replyingToEvent.eventId}">In reply to</a> <a href="https://matrix.to/#/${replyingToEvent.sender}">${replyingToEvent.sender}</a><br>${replyingToEvent.body}</blockquote></mx-reply>${body}`,
          msgtype: "m.text",
          'm.relates_to': {
            'm.in_reply_to': {
              event_id: replyingToEvent.eventId,
            },
          },
        };
        if (body !== '') {
          const date = new Date();
          const tnxId = date.getTime()/1000;
          document.getElementById("left").innerHTML = "Send";
          document.getElementById("right").innerHTML = "Menu";
          document.getElementById("middle").innerHTML = "ENTER";
          window.mClient.sendEvent(currentRoom, "m.room.message", content, tnxId).then(({event_id}) => {
            const room = window.mClient.getRoom(currentRoom);
            const newMsg = document.getElementById(`msg-~${room.roomId}:${tnxId}`);
            const newInnerMsg = document.getElementById(`innermsg-~${room.roomId}:${tnxId}`);
            const datetimeMsg = document.getElementById(`datetime-~${room.roomId}:${tnxId}`);
            newMsg.id = `msg-${event_id}`;
            newInnerMsg.id = `innermsg-${event_id}`;
            datetimeMsg.id = `datetime-${event_id}`;
            datetimeMsg.innerHTML = datetimeMsg.getAttribute('datetime');
            return;
          }).catch((err) => {
            alert('Failed to send message: ' + err.message);
            if(err.name === 'UnknownDeviceError') {
              verifyDevices();
            }
            console.log(err);
          });
          document.getElementById('chat-textbox').value = '';
          document.getElementById("chat-textbox").style.height = "24px";
          document.getElementById('chat-textbox').selectionStart = 0
          document.getElementById('conversation').style.height = '100%';
          document.getElementById('editing').style.bottom = "55px";
          replying.style.display = 'none';
          replyingToEvent = null;
        }
      } else {
        const body = document.getElementById('chat-textbox').value.trim();
        const content = {
          body,
          msgtype: "m.text"
        };
        if (body !== '') {
          // document.getElementById("chat-textbox").blur();
          // document.getElementById("chat-textbox").disabled = true;
          // document.getElementById("left").innerHTML = "";
          // document.getElementById("right").innerHTML = "";
          // document.getElementById("middle").innerHTML = "SENDING...";
          const date = new Date();
          const tnxId = date.getTime()/1000;
          window.mClient.sendEvent(currentRoom, "m.room.message", content, tnxId).then(({event_id}) => {
            const room = window.mClient.getRoom(currentRoom);
            const newMsg = document.getElementById(`msg-~${room.roomId}:${tnxId}`);
            const newInnerMsg = document.getElementById(`innermsg-~${room.roomId}:${tnxId}`);
            const datetimeMsg = document.getElementById(`datetime-~${room.roomId}:${tnxId}`);
            newMsg.id = `msg-${event_id}`;
            newInnerMsg.id = `innermsg-${event_id}`;
            datetimeMsg.id = `datetime-${event_id}`;
            datetimeMsg.innerHTML = datetimeMsg.getAttribute('datetime');
            return;
          }).catch((err) => {
            alert('Failed to send message: ' + err.message);
            if(err.name === 'UnknownDeviceError') {
              verifyDevices();
            }
          });
          document.getElementById('chat-textbox').value = '';
          document.getElementById("chat-textbox").style.height = "24px";
          document.getElementById('chat-textbox').selectionStart = 0
          document.getElementById('conversation').style.height = '100%';
          document.getElementById('replying').style.bottom = "55px";
        }
      }
    } else if ((e.key === "SoftRight" || e.key === "Shift") && page === 'chat' && document.getElementById("right").innerHTML !== '') { // cancel edit
      if(editing.style.display === 'unset' || replying.style.display === 'unset') {
        editing.style.display = 'none';
        replying.style.display = 'none';
        document.getElementById('chat-textbox').value = '';
        document.getElementById('chat-textbox').selectionStart = 0
        document.getElementById("chat-textbox").style.height = "24px";
        document.getElementById('editing').style.bottom = "55px";
        document.getElementById('replying').style.bottom = "55px";
        document.getElementById("left").innerHTML = "Send";
        document.getElementById("right").innerHTML = "Menu";
        document.getElementById("middle").innerHTML = "ENTER";
        editingEvent = null;
        replyingToEvent = null;
        const msg = document.getElementsByClassName('msgItem msgItem-selected')[0];
        msg.className = 'msgItem';
        highlightedMsg = null;
      } else {
        mainMenu = [
          {
            label: 'Send Attachment',
            fn: () => {
              new Promise((res, rej) => {
                try {
                  const file = new MozActivity({name: 'pick'});
                  file.onsuccess = function () {
                    const f = this.result;
                    res(f);
                  };
                  reader.onerror = function(e) {
                    alert('Failed to read file.');
                  };
                file.onerror = function() {
                  alert('Failed to select file.');
                };
              } catch {
                var input = document.createElement('input');
                input.type = 'file';
                input.onchange = e => {
                  var f = e.target.files[0];
                  res(f);
                }
                input.click();
              }
              }).then((f) => {
                new Promise((res, rej) => {
                  if(f.type.indexOf('image') > -1) {
                    const reader = new FileReader();
                    reader.onload = () => {
                      const img = new Image();
                      img.onload = () => {
                        return res(img);
                      };
                      img.src = reader.result;
                    };
                    reader.readAsDataURL(f);
                  } else {
                    res(null);
                  }
                }).then((img) => {
                  closeMenu(backToChat);
                  uploadContent(f, img).then((content) => {
                    const date = new Date();
                    const tnxId = date.getTime()/1000;
                    window.mClient.sendEvent(currentRoom, "m.room.message", content, tnxId).then(({event_id}) => {
                      const room = window.mClient.getRoom(currentRoom);
                      const newMsg = document.getElementById(`msg-~${room.roomId}:${tnxId}`);
                      const newInnerMsg = document.getElementById(`innermsg-~${room.roomId}:${tnxId}`);
                      const img = document.getElementById(`image-~${room.roomId}:${tnxId}`);
                      const datetimeMsg = document.getElementById(`datetime-~${room.roomId}:${tnxId}`);
                      newMsg.id = `msg-${event_id}`;
                      newInnerMsg.id = `innermsg-${event_id}`;
                      datetimeMsg.id = `datetime-${event_id}`;
                      datetimeMsg.innerHTML = datetimeMsg.getAttribute('datetime');
                      img.id = `image-${event_id}`
                    }).catch((e) => {
                      console.log(e);
                    });
                  });
                });
              });
            },
          },
          {
            label: 'Insert ☺️',
            fn: insertIntoTextbox('☺️'),
          },
          {
            label: 'Insert 😛️',
            fn: insertIntoTextbox('😛️️'),
          },
          {
            label: 'Insert 😕️',
            fn: insertIntoTextbox('😕️️'),
          },
          {
            label: 'Insert 👉👈',
            fn: insertIntoTextbox('👉👈'),
          },
          {
            label: 'Insert 👀',
            fn: insertIntoTextbox('👀'),
          },
          {
            label: 'Insert 🤔',
            fn: insertIntoTextbox('🤔'),
          },
        ];
        previousPage = 'chat';
        openMenu();
      }
    }
  } else {
    e.preventDefault();
    switch (e.key) {
    case "F1":
    case "Call": {
      if(page === 'chat') {
        const msgItem = document.getElementsByClassName('msgItem msgItem-selected')[0];
        if(msgItem) {
          const eventId = msgItem.id.substring(4);
          const innerMsg = document.getElementById(`innermsg-${eventId}`);
          let msg = innerMsg.innerText.trim();
          if(innerMsg.getElementsByClassName('edited-marker').length > 0) {
            msg = msg.slice(0, -9); // removed the "\n(edited)" part
          }
          const replying = document.getElementById('replying');
          replying.style.display = 'unset';
          replyingToEvent = {
            eventId,
            sender: msgItem.getAttribute('sender'),
            body: msg,
          };
          const chatBox = document.getElementById('chat-textbox');
          chatBox.focus();
          highlightedMsg += 1;
          if(chatBox.scrollHeight < 122) {
            chatBox.style.height = "auto";
            chatBox.style.height = (chatBox.scrollHeight) + "px";
            document.getElementById('editing').style.bottom = (chatBox.scrollHeight + 55 - 29) + "px";
            document.getElementById('replying').style.bottom = (chatBox.scrollHeight + 55 - 29) + "px";
          }
        }
      }
      return;
    }
    case "End":
    case "EndCall": {
      const chatBox = document.getElementById('chat-textbox');
      if(chatBox && chatBox.value !== '') {
        if(confirm('Are you sure you want to close the app?')) {
          window.mClient.stopClient();
          close();
        }
        return;
      }
      window.mClient.stopClient();
      close();
      return;
    }
    case "Backspace": {
      if(page === 'chat') {
        if(navigator.b2g && navigator.b2g.virtualCursor.enabled) {
          navigator.b2g.virtualCursor.disable();
        } else {
          gotoRoomView();
        }
      } else if (page === 'image') {
        const player = document.getElementById('big-video');
        if(player) {
          if(document.fullscreenElement && document.fullscreenElement !== null) {
            return document.exitFullscreen();
          }
          player.removeEventListener('ended', swapMiddle);
        }
        const conv = document.getElementById('conversation');
        conv.innerHTML = previousConv;
        previousConv = null;
        page = 'chat'
        if(currentlyWatching) {
          URL.revokeObjectURL(currentlyWatching);
          currentlyWatching = null;
        }
        const msgItem = document.getElementsByClassName('msgItem msgItem-selected')[0];
        if(msgItem) {
          msgItem.scrollIntoView({
            behavor: 'smooth',
            block: 'center',
            inline: 'nearest'
          });
        }
        document.getElementById("left").innerHTML = "Reply";
        document.getElementById("right").innerHTML = "Edit";
        document.getElementById("middle").innerHTML = "SELECT";
      } else if (page === 'menu') {
        if(previousPage === 'rooms') {
          closeMenu(gotoRoomView);
        } else {
          closeMenu(backToChat)
        }
      }
      return;
    }
    case "Enter": {
      if(page === 'rooms') {
        gotoChat();
        page = 'chat';
      } else if(page === 'chat') {
        const msgItem = document.getElementsByClassName('msgItem msgItem-selected')[0];
        if(msgItem) {
          const eventId = msgItem.id.substring(4);
          const image = document.getElementById(`image-${eventId}`)
          if(image) {
            const video = image.getAttribute('video');
            const audio = image.getAttribute('audio');
            if(video) {
              document.getElementById('left').innerHTML = 'Fullscreen'
              document.getElementById('middle').innerHTML = 'PLAY'
              document.getElementById('right').innerHTML = 'Mute'
              const content = JSON.parse(video);
              const imageUrl = content.url ? window.mClient.mxcUrlToHttp(content.url) : window.mClient.mxcUrlToHttp(content.file.url);
              loadingHandler();
              fetch(imageUrl).then((res) => {
                res.arrayBuffer().then((a) => {
                  const uploading = document.getElementById('uploading');
                  if(uploading) {
                    uploading.style.display = 'none';
                  }
                  if(content.file) {
                    decryptAttachment(a, content.file).then((dataArray) => {
                      let mimetype = content.info.mimetype ? content.info.mimetype.split(";")[0].trim() : '';
                      mimetype = getBlobSafeMimeType(mimetype);
                      const blob = new Blob([dataArray], { type: mimetype });
                      const blobUrl = URL.createObjectURL(blob);
                      currentlyWatching = blobUrl;
                      const conv = document.getElementById('conversation');
                      previousConv = conv.innerHTML;
                      conv.innerHTML = `
<video id="big-video"/>
<source src="${blobUrl}">
Unsupported Video
</video>
`;
                      page = 'image';
                    });
                  } else {
                    let mimetype = content.info.mimetype ? content.info.mimetype.split(";")[0].trim() : '';
                    mimetype = getBlobSafeMimeType(mimetype);
                    const blob = new Blob([a], { type: mimetype });
                    const blobUrl = URL.createObjectURL(blob);
                    currentlyWatching = blobUrl;
                    const conv = document.getElementById('conversation');
                    previousConv = conv.innerHTML;
                    conv.innerHTML = `
<video id="big-video"/>
<source src="${blobUrl}">
Unsupported Video
</video>
`;
                    page = 'image';
                  }
                });
              });
            } else if (audio) {
              const content = JSON.parse(audio);
              const imageUrl = content.url ? window.mClient.mxcUrlToHttp(content.url) : window.mClient.mxcUrlToHttp(content.file.url);
              document.getElementById('left').innerHTML = 'Fullscreen'
              document.getElementById('middle').innerHTML = 'PLAY'
              document.getElementById('right').innerHTML = 'Mute'
              loadingHandler();
              fetch(imageUrl).then((res) => {
                res.arrayBuffer().then((a) => {
                  const uploading = document.getElementById('uploading');
                  if(uploading) {
                    uploading.style.display = 'none';
                  }
                  if(content.file) {
                    decryptAttachment(a, content.file).then((dataArray) => {
                      let mimetype = content.info.mimetype ? content.info.mimetype.split(";")[0].trim() : '';
                      mimetype = getBlobSafeMimeType(mimetype);
                      const blob = new Blob([dataArray], { type: mimetype });
                      const blobUrl = URL.createObjectURL(blob);
                      currentlyWatching = blobUrl;
                      const conv = document.getElementById('conversation');
                      previousConv = conv.innerHTML;
                      conv.innerHTML = `
<audio id="big-video"/>
<source src="${blobUrl}">
Unsupported Video
</audio>
`;
                      page = 'image';
                    });
                  } else {
                    let mimetype = content.info.mimetype ? content.info.mimetype.split(";")[0].trim() : '';
                    mimetype = getBlobSafeMimeType(mimetype);
                    const blob = new Blob([a], { type: mimetype });
                    const blobUrl = URL.createObjectURL(blob);
                    currentlyWatching = blobUrl;
                    const conv = document.getElementById('conversation');
                    previousConv = conv.innerHTML;
                    conv.innerHTML = `
<audio id="big-video"/>
<source src="${blobUrl}">
Unsupported Video
</audio>
`;
                    page = 'image';
                  }
                })
              });
            } else {
              const imageUrl = image.getAttribute('src');
              const conv = document.getElementById('conversation');
              previousConv = conv.innerHTML;
              conv.innerHTML = `<img id="big-image" src="${imageUrl}"/>`;
              page = 'image';
              document.getElementById('left').innerHTML = 'Zoom Out'
              document.getElementById('middle').innerHTML = ''
              document.getElementById('right').innerHTML = 'Zoom In'
            }
          } else {
            navigator.b2g.virtualCursor.enable();
          }
        }
      } else if(page === 'menu') {
        const item = mainMenu[highlightedMenuItem];
        if(item) {
          item.fn();
        }
      } else if(page === 'image') {
        const player = document.getElementById('big-video');
        if(player) {
          if(player.paused) {
            document.getElementById('middle').innerHTML = 'PAUSE';
            player.play();
            player.addEventListener('ended', swapMiddle)
          } else {
            document.getElementById('middle').innerHTML = 'PLAY';
            player.pause();
            player.removeEventListener('ended', swapMiddle)
          }
        }
      }
      return;
    }
    case "ArrowUp": {
      if(page === 'rooms') {
        const cur = rooms[highlightedRoom];
        let num = highlightedRoom - 1
        if(num < 0) {
          num = rooms.length - 1;
        }
        const next = rooms[num];
        cur.className = "roomItem";
        next.className = "roomItem active";
        highlightedRoom = num;
      } else if(page === 'chat') {
        var msgs = document.getElementsByClassName('msgItem');
        if (highlightedMsg === 0) {
          document.getElementById('left').innerHTML = ''
          document.getElementById('middle').innerHTML = 'Loading...'
          document.getElementById('right').innerHTML = ''
          const room = window.mClient.getRoom(currentRoom);
          const timeline = room.getLiveTimeline();
          const originalLength = msgs.length;
          loadingMessagesIndex = 10;
          window.mClient.paginateEventTimeline(timeline, {backwards: true, limit: 10}).then(() => {
            document.getElementById('left').innerHTML = 'Reply'
            document.getElementById('middle').innerHTML = 'SELECT';
            document.getElementById('right').innerHTML = 'Edit'
          });
          return;
        } else if(highlightedMsg !== msgs.length) {
          msgs[highlightedMsg].className = 'msgItem';
        }
        highlightedMsg = highlightedMsg - 1;
        msgs[highlightedMsg].className = 'msgItem msgItem-selected';
        msgs[highlightedMsg].scrollIntoView({
          behavor: 'smooth',
          block: 'nearest',
          inline: 'nearest'
        });
      } else if (page === 'image') {
        const conv = document.getElementById('conversation');
        conv.scrollBy({
          top: -100,
        });
      } else if (page === 'menu') {
        const menuItems = document.getElementsByClassName('menuItem');
        const cur = menuItems[highlightedMenuItem];
        let num = highlightedMenuItem - 1
        if(num < 0) {
          num = menuItems.length - 1;
        }
        const next = menuItems[num];
        cur.className = "menuItem";
        next.className = "menuItem active";
        highlightedMenuItem = num;
      }
      return;
    }
    case "ArrowDown": {
      if(page === 'rooms') {
        const cur = rooms[highlightedRoom];
        let num = highlightedRoom + 1
        if(num > rooms.length - 1) {
          num = 0;
        }
        const next = rooms[num];
        cur.className = "roomItem";
        next.className = "roomItem active";
        highlightedRoom = num;
      } else if (page === 'chat') {
        var msgs = document.getElementsByClassName('msgItem');
        var mNext = highlightedMsg + 1;
        if(mNext === msgs.length && !scrollMsg) {
          msgs[highlightedMsg].className = 'msgItem';
          highlightedMsg = msgs.length;
          document.getElementById('chat-textbox').focus();
        } else {
          if ((msgs[mNext] && msgs[mNext].clientHeight > document.getElementById('conversation').clientHeight) || scrollMsg) {
            if(!scrollMsg) {
              scrollMsg = msgs[mNext].clientHeight;
              document.getElementById('conversation').scrollBy({
                top: 100,
                left: 0,
                behavor: 'smooth',
              });
              scrollMsg = scrollMsg - 100;
            } else if(scrollMsg <= document.getElementById('conversation').clientHeight - 100) {
              scrollMsg = null;
            } else {
              document.getElementById('conversation').scrollBy({
                top: 100,
                left: 0,
                behavor: 'smooth',
              });
              scrollMsg = scrollMsg - 100;
              return;
            }
          }

          if (mNext >= msgs.length) {
            msgs[highlightedMsg].className = 'msgItem';
            document.getElementById('chat-textbox').focus();
          } else {
            msgs[highlightedMsg].className = 'msgItem';
            highlightedMsg = mNext
            msgs[highlightedMsg].className = 'msgItem msgItem-selected';
            msgs[highlightedMsg].scrollIntoView({
              behavor: 'smooth',
              block: 'nearest',
              inline: 'nearest'
            });
          }
        }
      } else if (page === 'image') {
        const conv = document.getElementById('conversation');
        conv.scrollBy({
          top: 100,
        });
      } else if (page === 'menu') {
        const menuItems = document.getElementsByClassName('menuItem');
        const cur = menuItems[highlightedMenuItem];
        let num = highlightedMenuItem + 1
        if(num === menuItems.length) {
          num = 0
        }
        const next = menuItems[num];
        cur.className = "menuItem";
        next.className = "menuItem active";
        highlightedMenuItem = num;
      }
      return;
    }
    case "ArrowLeft": {
      if (page === 'image') {
        const conv = document.getElementById('conversation');
        conv.scrollBy({
          left: -100,
        });
      }
      return;
    }
    case "ArrowRight": {
      if (page === 'image') {
        const conv = document.getElementById('conversation');
        conv.scrollBy({
          left: 100,
        });
      }
      return;
    }
    case "Shift":
    case "SoftRight": {
      if(page === 'chat') {
        const msgItem = document.getElementsByClassName('msgItem msgItem-selected')[0];
        if(msgItem) {
          const eventId = msgItem.id.substring(4);
          const innerMsg = document.getElementById(`innermsg-${eventId}`);
          if(innerMsg.style['align-self'] === 'flex-end') {
            let msg = innerMsg.getAttribute('body').trim();
            document.getElementById('chat-textbox').value = msg;
            const editing = document.getElementById('editing');
            editing.style.display = 'unset';
            editingEvent = eventId;
            const chatBox = document.getElementById('chat-textbox');
            chatBox.focus();
            highlightedMsg += 1;
            if(chatBox.scrollHeight < 122) {
              chatBox.style.height = "auto";
              chatBox.style.height = (chatBox.scrollHeight) + "px";
              document.getElementById('editing').style.bottom = (chatBox.scrollHeight + 55 - 29) + "px";
              document.getElementById('replying').style.bottom = (chatBox.scrollHeight + 55 - 29) + "px";
            }
          }
        }
      } else if (page === 'image') {
        const image = document.getElementById('big-image');
        const player = document.getElementById('big-video');
        if(image) {
          const height = Number(image.getAttribute('height'));
          image.setAttribute('height', 25 + (height ? height : image.scrollHeight))
          const width = Number(image.getAttribute('width'));
          image.setAttribute('width', 25 + (width ? width : image.scrollWidth));
        } else if(player) {
          if(player.muted) {
            document.getElementById("right").innerHTML = "Mute";
            player.muted = false;
          } else {
            document.getElementById("right").innerHTML = "Unmute";
            player.muted = true;
          }
        }
      } else if (page === 'rooms') {
        mainMenu = [
          {
            label: 'Verify Device',
            fn: verifyDevice,
          },
          {
            label: 'Logout',
            fn: killSession,
          },
        ];
        previousPage = 'rooms';
        openMenu();
      } else if (page === 'menu') {
        if(previousPage === 'rooms') {
          closeMenu(gotoRoomView);
        } else {
          closeMenu(backToChat);
        }
      }
      return;
    }
    case "Control":
    case "SoftLeft": {
      if(page === 'chat') {
        const msgItem = document.getElementsByClassName('msgItem msgItem-selected')[0];
        if(msgItem) {
          const eventId = msgItem.id.substring(4);
          const innerMsg = document.getElementById(`innermsg-${eventId}`);
          let msg = innerMsg.innerText.trim();
          if(innerMsg.getElementsByClassName('edited-marker').length > 0) {
            msg = msg.slice(0, -9); // removed the "\n(edited)" part
          }
          const replying = document.getElementById('replying');
          replying.style.display = 'unset';
          replyingToEvent = {
            eventId,
            sender: msgItem.getAttribute('sender'),
            body: msg,
          };
          const chatBox = document.getElementById('chat-textbox');
          chatBox.focus();
          highlightedMsg += 1;
          if(chatBox.scrollHeight < 122) {
            chatBox.style.height = "auto";
            chatBox.style.height = (chatBox.scrollHeight) + "px";
            document.getElementById('editing').style.bottom = (chatBox.scrollHeight + 55 - 29) + "px";
            document.getElementById('replying').style.bottom = (chatBox.scrollHeight + 55 - 29) + "px";
          }
        }
      } else if (page === 'image') {
        const image = document.getElementById('big-image');
        const player = document.getElementById('big-video');
        if(image) {
          const height = Number(image.getAttribute('height'));
          image.setAttribute('height', (height ? height : image.scrollHeight) - 25 )
          const width = Number(image.getAttribute('width'));
          image.setAttribute('width', (width ? width : image.scrollWidth) - 25);
        } else if (player) {
          if(document.fullscreenElement && document.fullscreenElement !== null) {
            document.exitFullscreen();
          } else {
            player.requestFullscreen();
          }
        }
      }
      return;
    }
    case "1": {
      sendReaction('👍');
      return;
    }
    case "2": {
      sendReaction('👎');
      return;
    }
    case "3": {
      sendReaction('❤️');
      return;
    }
    case "4": {
      sendReaction('🔥');
      return;
    }
    case "5": {
      sendReaction('😁');
      return;
    }
    case "6": {
      sendReaction('🎉');
      return;
    }
    case "7": {
      sendReaction('🤣');
      return;
    }
    case "8": {
      sendReaction('🤔');
      return;
    }
    case "9": {
      sendReaction('🤯');
      return;
    }
    case "0": {
      sendReaction('👀');
      return;
    }
    case "#": {
      sendReaction('😭');
      return;
    }
    case "*": {
      sendReaction('💩');
      return;
    }
    }
  }
};

document.addEventListener("DOMContentLoaded", async () => {
  const session = localStorage.getItem(LOCAL_STORAGE_SESSION_KEY);
  if(session) {
    const client = JSON.parse(session);
    prepareSession(client);
  } else {
	  document.getElementById("title-text").innerHTML = 'Login';
    document.getElementById("login").style = 'display: unset';
    document.getElementById("left").innerHTML = "";
    document.getElementById("right").innerHTML = "";
    document.getElementById("middle").innerHTML = "LOGIN";
    document.getElementById('homeserver').focus();
  }

  const chatBox = document.getElementById('chat-textbox');
  chatBox.addEventListener("focus", (e) => {
    const editing = document.getElementById('editing');
    const replying = document.getElementById('replying');
    if(editing.style.display === 'unset') { // is editting
      document.getElementById("left").innerHTML = "Save";
      document.getElementById("right").innerHTML = "Discard";
      document.getElementById("middle").innerHTML = "ENTER";
    } else if (replying.style.display === 'unset') {
      document.getElementById("left").innerHTML = "Send";
      document.getElementById("right").innerHTML = "Discard";
      document.getElementById("middle").innerHTML = "ENTER";
    } else {                    // new msg
      document.getElementById("left").innerHTML = "Send";
      document.getElementById("right").innerHTML = "Menu";
      document.getElementById("middle").innerHTML = "ENTER";
      const scrollHeight = document.getElementById("conversation").scrollHeight;
      document.getElementById("conversation").scrollTo(0, scrollHeight);
    }
  });

  chatBox.addEventListener("blur", (e) => {
    if(page === 'chat') {
      document.getElementById("left").innerHTML = "Reply";
      document.getElementById("right").innerHTML = "Edit";
      document.getElementById("middle").innerHTML = "SELECT";
    }
  });

  // auto resize the text boxes
  const tx = document.getElementsByTagName("textarea");
  for (let i = 0; i < tx.length; i++) {
    tx[i].setAttribute("style", "height:24px;overflow-y:hidden;");
    tx[i].addEventListener("input", OnInput, false);
  };

  function OnInput() {
    if(this.scrollHeight < 122) {
      this.style.height = "auto";
      this.style.height = (this.scrollHeight) + "px";
      const conv = document.getElementById("conversation");
      conv.style.height = `calc(100% - ${this.scrollHeight + 52}px)`;
      if(document.getElementById('editing').style.display === 'none'
         &&
         document.getElementById('replying').style.display === 'none') {
        conv.scrollTo(0, conv.scrollHeight);
      }
      document.getElementById('editing').style.bottom = (this.scrollHeight + 55 - 29) + "px";
      document.getElementById('replying').style.bottom = (this.scrollHeight + 55 - 29) + "px";
    }
  };


});

const gotoChat = () => {
  const roomEl = document.getElementsByClassName('roomItem active')[0];
  const id = roomEl.id.substring(5);
  const room = window.mClient.getRoom(id);
  // const timeline = room.getLiveTimeline();
  // const rest = window.mClient.paginateEventTimeline(timeline, {backwards: true}).then((d) => {
  const events = window.mClient.getRoom(id).timeline;
  var relations = [];
  const eventList = events.reduce((a, event) => {
    const relation = event.getRelation();
    if(relation) {
      relations.push({event, relation});
      return a + '';
    }
    if (event.getType() === 'm.room.message') {
      // window.mClient.decryptEventIfNeeded(event, {isRetry: false, emit: true}).then((d) => {
        // const events = window.mClient.getRoom(id).timeline;
      //   console.log('DECRYPTED: ', d);
      // })
      return a + messageBuilder(event, room);
    }
    return a + '';
  }, '');
  document.getElementById("conversation").innerHTML = eventList;
  relations.forEach(({event, relation}) => {
    const {
      event_id,
      rel_type,
    } = relation;
    const msgItem = document.getElementById(`msg-${event_id}`);
    if(msgItem) {
      if(rel_type === 'm.annotation') {
        markReaction(event, relation);
      } else if(rel_type === 'm.replace') {
        replaceBody(event, relation);
      }
    }
    return;
  });
  document.getElementById('room-menu').style = 'display: none';
  document.getElementById('chat').style = 'display: flex';
  document.getElementById('chat-textbox').value = '';
  document.getElementById('chat-textbox').focus();
	document.getElementById("title-text").innerHTML = room.name;
  document.getElementById("left").innerHTML = "Send";
  document.getElementById("right").innerHTML = "Menu";
  document.getElementById("middle").innerHTML = "ENTER";
  setTimeout(() => {
    const scrollHeight = document.getElementById("conversation").scrollHeight;
    document.getElementById("conversation").scrollTo(0, scrollHeight);
  }, 500);
  currentRoom = id;
};

document.addEventListener("keydown", keydownListener);

document.addEventListener('click', (event) => {
  if(event.target.localName === 'a') {
    if(confirm('Open in popup?')) {
      event.preventDefault();
      window.open(event.target.href);
    }
  }
});
